#include "tm_processor.h"

Processor::~Processor()
{
	//for_each(lines.begin,lines.end, myobject);  compile error, why?
	for(vector <TrackLine*>::iterator it = lines.begin();it!= lines.end();++it)
	{
		displayLine( *(*it) );
		delete *it;
	}
}

bool Processor::loadFile()
{
    ifstream fin(fileName.c_str());

    if(!fin){
        throw Error("input file not exist");
    }

    string line;
    float getTime;
    Status getStat;
    string getSectionName;
    //load, check input, then merge into lines collector.
    while(getline(fin,line)){
        if(trim(line) == false)
            continue;

        if( validate(line, getTime,getStat, getSectionName) == true)
            merge( getTime,getStat, getSectionName);
    }
    
    fin.close();
    return true;
}

bool Processor::trim(std::string &s){
    if (s.empty()) {
        return false;
    }
    unsigned L_idx = s.find_first_of("[[");
    unsigned R_idx = s.find_last_of("]]");

    if( L_idx == string::npos || R_idx == string::npos)
        return false;

    s.erase(0,L_idx +2);
    s.erase(s.find_last_of("]]") - 1);
    return true;
}

bool Processor::validate(const string s, float& sTime, Status & stat, string& sName)
{
    //cout<<"Input:"<<s<<endl;

    string sStatus; 
    istringstream stream(s);
    stream>>sName>>sStatus>>sTime;

    if(sStatus == "B"){
        stat = BEGIN_OR_ALIVE;
    }
    else if(sStatus == "E"){
        stat  =END_OR_WAIT;
    }
    else{
        PR("unexpected SStatus, drop one log:"<<sStatus);
        return false;
    }

    if(sTime < 0){
        PR("unexpected sTime, drop one log:"<<sStatus);
        return false;
    }

    //PARAM_COUT(sName);
    //PARAM_COUT(sStatus);
    //PARAM_COUT(sTime);
    return true;
}

void Processor::merge(const float& sTime, const Status & sStat,const string& sectionName)
{
    vector <TrackLine*>::iterator it;
    for(it = lines.begin(); it!= lines.end();it++) //item belong to existing item.
    {
        if((*it)->name == sectionName){
            //cout<<">>merge into:"<<sectionName<<" sTime:"<<sTime<<endl;
            (*it)->timeStamps[sTime] = sStat;
            break;
        }
    }
    if( it == lines.end() ) //item belong to new time line.
    {	
        TrackLine * newLine = new TrackLine();
        newLine->name = sectionName;
        newLine->timeStamps[sTime] = sStat;

        lines.push_back(newLine);
        cout<<"++Add newTrackLine:"<<sectionName<<" init sTime:"<<sTime<<endl;
    }
}	

void Processor::displayLine(TrackLine & in)
{
	cout<< in.name<<", timeStame Count:"<<in.timeStamps.size()<<", slicedInfos Count:"
	<<in.slotInfos.size()<<endl;
    /* //UT purpose. 20130825
	for(map<float,Status>::iterator it = in.timeStamps.begin(); it != in.timeStamps.end(); ++it)
	{
		cout<<"   "<<fixed<<setprecision(2)<<it->first<<"_"<< 
			((it->second==BEGIN_OR_ALIVE)?"BEGIN_OR_ALIVE":"END_OR_WAIT")<<endl;
	}

	for(vector<SlotInfo> ::iterator it = in.slotInfos.begin();it!=in.slotInfos.end();++it)
	{
		cout<<" ----"<< it->slotBeginTime<<"_"<< it->status<<endl;
	}
   */
}

//make lots of timeSlot, each slot represent status within slotDuration. 
//Fill the slot as 1/0 if the tracked item is BEGIN_OR_ALIVEEND_OR_WAIT
//***
//pre-condition: Input TrackLine has been sorted. 
bool Processor::parseSlots()
{
	//Find the earlyest and latest timeStamp.
	//It's eaiser to generate output file if all TrackLines have same slots, 
	float earlyestStamp;
	float latestStamp;
	set<float> sortItems;
	for(vector <TrackLine*>::iterator it = lines.begin();it!= lines.end();++it)
	{
		sortItems.insert((*it)->timeStamps.begin()->first);
		sortItems.insert((*it)->timeStamps.rbegin()->first);
	}

	earlyestStamp = *sortItems.begin();
	latestStamp = *sortItems.rbegin();
	PARAM_COUT(earlyestStamp);
	PARAM_COUT(latestStamp);	
	assert(latestStamp > earlyestStamp &&  earlyestStamp > 0);
	
	for(vector <TrackLine*>::iterator it = lines.begin();it!= lines.end();++it)
	{
		parseOneLine( *it, earlyestStamp, latestStamp);
	}
    
	return true;
}

bool Processor:: parseOneLine(TrackLine* tline, float trackAreaBegin, float trackAreaEnd)
{
	if(tline->timeStamps.size()  < 2)
		return false;
	int processedSlot = 0;
	
	vector<SlotInfo> & slots = ( *tline).slotInfos;
	
	map<float,Status>::iterator itStamp = tline->timeStamps.begin();
	//it_end not used as now we use global latest timeStamp.
	//map<float,Status>::reverse_iterator it_end = tline->timeStamps.rbegin();

	// 1. Create slots based on timeStamp range
	slotCount = createSlots(trackAreaBegin, trackAreaEnd, slots);
	assert(slotCount > 1);
	
	// 2. Mark slot's status accordingly
	Status currStatus = END_OR_WAIT;
	float slotDelta = float(slotDuration)/1000;
	for(	vector<SlotInfo>::iterator itSlot=slots.begin(); itSlot!= slots.end() &&
		itStamp !=  tline->timeStamps.end(); )
	{
		float stampTime = itStamp->first;
		float slotBeginTime = itSlot->slotBeginTime;
	
		//Three conditions:
		/*                 |       stampTime              |*/
		/*slot range: |  ~ less)  [belong)  [greater ~  | */
		// 1. LESS:   	stampTime < slot Begin
		// 2. Belong: 	slotBegin  <= stampTime < slotBegin+Delta
		// 3. Greater: 	stampTime  > slotBegin+ delta


		//this happeneds when everal event happened at same slots
		//in this scenario, only the first event's status is used.
		if( stampTime < slotBeginTime ){
			++itStamp;
			PR(" stampTime < slotBeginTime, jump to next stamp/event,"<<stampTime
				<<"_"<< slotBeginTime);
			continue;
		}

		//event happened during this slot
		//eg: [mainthread B 1.8], slots: [1.0] [1.5] [2.0]
		//A event happened in [1.5] slot.
		if(stampTime >= slotBeginTime && stampTime < (slotBeginTime + slotDelta) )
		{
			if(itSlot->status == currStatus){
				if(itStamp  == tline->timeStamps.begin())
					;//first event  may have same status as initial stats.
				else
					PR("un-matched event Status, igore it."<<tline->name<<"@"<<stampTime);
			}
			
			itSlot->status = currStatus; 
		
			currStatus = itStamp->second;
			
			//If it's BeginXXX , set status from this slot. If it's EndXXX, mark it from next slot
			if(currStatus == BEGIN_OR_ALIVE)
				itSlot->status = currStatus; 

			++itStamp;
			++itSlot;
			++processedSlot;
			continue;
		}

		if( stampTime >= (slotBeginTime + slotDelta) )
		{
			itSlot->status = currStatus; 
			++itSlot;
			++processedSlot;
			continue;
		}

		PR("error logic, process will exit");
		PARAM_COUT(stampTime);
		PARAM_COUT(slotBeginTime);
		PARAM_COUT(slotDelta);
		assert(0);
		break;
	}
		
	return processedSlot;
}


// findSlot(3.4), slotDuration = 500(ms), retv= 3.00
// findSlot(3.4), slotDuration = 333(ms), retv= 3.33
// findSlot(3.4), slotDuration = 2000(ms), retv= 2
float Processor::findSlot(float input)
{
	return 1.0;	
}

int Processor::createSlots(float begin,float end, vector<SlotInfo> & slots)
{
	begin = floor(begin);
	end = ceil(end);
	
	if(begin >= end )
	{
		PR("unexpected data, begin time should < end time. (begin - end) "<<begin<<" - "<<end);
		return 0;
	}

	long slots_num = (end - begin)*1000 /float(slotDuration);

	assert(slots_num > 0);
	if(slots_num > MAX_SLOTS)
		slots_num = MAX_SLOTS;
	
	SlotInfo cell;
	float timeDelta = float(slotDuration) /1000; //ms to second
	float lastCellTime = begin - timeDelta;//minus for first cell.
	for(int i = 0; i < slots_num; i ++)
	{
		cell.status = END_OR_WAIT;
		cell.slotBeginTime = lastCellTime+timeDelta;
		lastCellTime = cell.slotBeginTime;
		slots.push_back(cell);
	}
	return slots_num;
}

bool Processor::output()
{
        cout<<"Begin to output file"<<endl;
 
        unsigned found = fileName.find_last_of("/\\");
        string outFileName = fileName.substr(found+1);
        outFileName.append(output_appendx);
        PARAM_COUT(outFileName);
        
	ofstream of(outFileName.c_str());

	for(vector <TrackLine*>::iterator it = lines.begin();it!= lines.end();++it)
	{
		of<<output_delimiter<<(*it)->name;
	}
	of<<"\n";

       
	vector <TrackLine*>::iterator it = lines.begin();
	//assert each TrackLine have size.

	/*          ; trackLine1; Line2;
	*    10.5 ; 1        ;0
	*        11; 1        ;0
	*     11.5; 0        ;1
	*      ...
	*/

        for(; it!=lines.end();it++)
        {
            assert(slotCount == (*it)-> slotInfos.size());
        }
     
	for(unsigned i =0; i < slotCount; i++)
	{       
	        it = lines.begin();
	        of<<fixed<<setprecision(2)<<(*it)-> slotInfos[i].slotBeginTime;
		for(;it!= lines.end();it++)
		{
		    of<<output_delimiter<<(*it)-> slotInfos[i].status;
		}
                of<<"\n";
	}
    
	of.close();
        cout<<"Write file done!"<<endl;

	return true;	
}

